(function($)
{
	$.fn.videoplayer = function(width, height, controls)
	{
		// HTML5 브라우저별 지원 포맷
		var _HTML_BROWSER_SUPPORT_VIDEO = {'video/webm': 'msie,mozilla',
											'video/mp4': 'msie',
											'video/ogg': 'webkit,mozilla'};
		
		// HTML5 비디오 코덱
		var _HTML5_CODEC_MAP = {'video/webm': 'vp8, vorbis',
								'video/mp4': 'avc1.42E01E, mp4a.40.2',
								'video/ogg': 'theora, vorbis'};
		
		$.prototype.videoTag = Modernizr.video ? true : false;
		$.prototype.width = width;
		$.prototype.height = height;
		$.prototype.currentTime = 0;
		
		/**
		 * 플레이어를 초기화한다.
		 */
		$.prototype.initialize = function(controls)
		{
			this.attr('class', 'vp-container');
			
			this.css('width', this.width);
			this.css('height', this.height);
			
			if (controls)
			{
				this.renderControls();
				this.attachControlsEvent();
			}
		};
		
		/**
		 * HTML5 비디오 플레이어 이벤트를 연결한다.
		 */
		$.prototype.attachHtml5PlayerEvent = function()
		{
			this.player.mouseover({container: this}, function(e)
			{
				if (e.data.container.isPlaying())
				{
					e.data.container.showControls();
				}
			});
			
			this.player.mouseout({container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				var controls = player.controls;
				
				if (container.isPlaying() && controls.css('display') != 'block')
				{
					container.hideControls();
				}
			});
			
			// 재생 준비완료 이벤트
			this.player.bind('canplay', {container: this}, function(e)
			{
				var container = e.data.container;
				
				container.renderVolume(container.volume() * 100);
				
				container.renderDuration();
			});
			
			// 버퍼링 이벤트
			// * 브라우저에 따라 발생할 수도 있고 안 할수도 있다.
			this.player.bind('progress', {container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				var buffered = player[0].buffered;
				
				if (buffered.length > 0)
				{
					var rate = buffered.end(0) * 100.0 / e.currentTarget.duration;
					
					container.renderBuffer(rate);
				}
			});
			
			this.player.bind('playing', {container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				var controls = player.controls;
				
				controls.playbutton.attr('class', 'vp-controls-playbutton vp-controls-pausebutton');
				
				$(container).trigger('playing');
			});
			
			this.player.bind('pause', {container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				var controls = player.controls;
				
				controls.playbutton.attr('class', 'vp-controls-playbutton');
				
				$(container).trigger('pause');
			});
			
			this.player.bind('ended', {container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				var controls = player.controls;
				
				controls.playbutton.attr('class', 'vp-controls-playbutton');
				
				container.showControls();
				
				$(container).trigger('ended');
			});
			
			this.player.bind('timeupdate', {container: this}, function(e)
			{
				var time = e.target.currentTime;
				var rate = e.target.currentTime * 100 / e.target.duration;
				
				e.data.container.renderProgress(rate, time);
			});
			
			this.player.bind('contextmenu', function(e)
			{
				return false;
			});
		};
		
		/**
		 * HTML 비디오 태그에서 사용할 수 있는 비디오 포맷인지 검사한다.
		 */
		$.prototype.matchVideoAndBrowser = function(type)
		{
			var supportVideo = _HTML_BROWSER_SUPPORT_VIDEO[type];
			
			if ($.browser.msie)
			{
				return supportVideo.indexOf('msie') > -1;
			}
			else if ($.browser.webkit && $.browser.safari)
			{
				return supportVideo.indexOf('webkit') > -1;
			}
			else if ($.browser.mozilla)
			{
				return supportVideo.indexOf('mozilla') > -1;
			}
			
			return false;
		};
		
		/**
		 * 미디어 소스를 설정한다.
		 */
		$.prototype.setSource = function(src, type, play)
		{
			if (this.player)
			{
				this.player.remove();
			}
			
			// HTML 비디오 태그 재생이 가능한 경우
			if (this.videoTag && this.matchVideoAndBrowser(type))
			{
				$.prototype.playerType = 0;
				
				$.prototype.player = $('<video></video>').appendTo(this);
				
				this.player.css('width', this.width);
				this.player.css('height', this.height);
				
				this.attachHtml5PlayerEvent();
				
				var sourceDom = $("<source></source>").appendTo(this.player);
				
				sourceDom.attr('src', src);
				sourceDom.attr('type', type + "; codecs=\"" + _HTML5_CODEC_MAP[type] + "\"");

				if (play)
				{
					this.play();
				}
			}
			// 윈도우 미디어 플레이어를 이용하는 경우
			else
			{
				$.prototype.playerType = 1;
				
				if ($.browser.msie)
				{
					$.prototype.player = $('<object classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" type="application/x-oleobject"></object>').appendTo(this);
					
					this.player.attr('width', this.width);
					this.player.attr('height', this.height);
					
					$('<param name="URL" value="' + src + '">').appendTo(this.player);
					$('<param name="AutoStart" value="' + play + '">').appendTo(this.player);
					$('<param name="uiMode" value="none">').appendTo(this.player);
					$('<param name="enableContextMenu" value="false">').appendTo(this.player);
					$('<param name="stretchToFit" value="true">').appendTo(this.player);
				}
				else
				{
					$.prototype.player = $('<object type="application/x-ms-wmp"></object>').appendTo(this);
					
					this.player.attr('width', this.width);
					this.player.attr('height', this.height);
					
					$('<param name="src" value="' + src + '">').appendTo(this.player);
					$('<param name="AutoStart" value="' + play + '">').appendTo(this.player);
					$('<param name="ShowControls" value="0">').appendTo(this.player);
					$('<param name="ShowStatusBar" value="0">').appendTo(this.player);
					$('<param name="uiMode" value="none">').appendTo(this.player);
					$('<param name="enableContextMenu" value="false">').appendTo(this.player);
					$('<param name="stretchToFit" value="true">').appendTo(this.player);
					
					this.controls.css('bottom', -30);
				}
			}
			
			$(window).mouseover({container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				
				var offset = player.offset();
			
				var left = offset.left;
				var top = offset.top;
				var right = left + container.width;
				var bottom = top + container.height;
				
				if (e.clientX < left || e.clientX > right || e.clientY < top || e.clientY > bottom)
				{
					if (container.isPlaying())
					{
						container.hideControls();
					}
				}
			});
		};
		
		/**
		 * 미디어를 재생한다.
		 */
		$.prototype.play = function()
		{
			switch (this.playerType)
			{
				case 0:
					
					this.player[0].play();
					
					break;
					
				case 1:
					
					this.player[0].controls.play();
					
					break;
			}
		};
		
		/**
		 * 미디어가 재생 중인지 판단한다.
		 */
		$.prototype.isPlaying = function()
		{
			switch (this.playerType)
			{
				case 0:
					
					return !this.player[0].paused && !this.player[0].ended;
					
					break;
					
				case 1:
					
					return this.player[0].playState == 3;
					
					break;
			}
		};
		
		$.prototype.getWmpDownloadRate = function()
		{
			if (this.playerType == 1)
			{
				return this.player[0].network.downloadProgress;
			}
		};
		
		/**
		 * 재생 중인 미디어를 일시 정지한다.
		 */
		$.prototype.pause = function()
		{
			switch (this.playerType)
			{
				case 0:
					
					//this.player[0].pause();
					//this.player[0].
					
					break;
					
				case 1:
					
					this.player[0].controls.pause();
					
					break;
			}
		};
		
		/**
		 * 재생 중인 미디어를 정지한다.
		 */
		$.prototype.stop = function()
		{
			switch (this.playerType)
			{
				case 0:
					
					this.currentTime(0);
					
					this.player[0].pause();
					
					break;
					
				case 1:
					
					this.player[0].controls.stop();
					
					break;
			}
		};
		
		/**
		 * 재생 중인 미디어의 현재 재생 시간을 설정하거나 가져온다.
		 */
		$.prototype.currentTime = function(time)
		{
			if (time)
			{
				switch (this.playerType)
				{
					case 0:
						
						this.player[0].currentTime = time;
						
						break;
						
					case 1:
						
						this.player[0].controls.currentPosition = time;
						
						break;
				}
			}
			
			return this.playerType == 0 ? this.player[0].currentTime : this.player[0].controls.currentPosition;
		};
		
		/**
		 * 
		 */
		$.prototype.duration = function()
		{
			return this.playerType == 0 ? this.player[0].duration : this.player[0].currentMedia.duration;
		};
		
		/**
		 * 플레이어의 볼륨을 설정하거나 가져온다.
		 */
		$.prototype.volume = function(volume)
		{
			if (volume)
			{
				if (volume >= 0.0 || volume <= 1.0)
				{
					switch (this.videoTag)
					{
						case 0:
							
							this.player[0].volume = volume;
							
							break;
							
						case 1:
							
							this.player[0].settings.volume = volume * 100;
							
							break;
					}
				}
			}
			
			return this.playerType == 0 ? this.player[0].volume : this.player[0].settings.volume;
		};
		
		/**
		 * (HTML 비디오 태그)탐색이 가능한 구간인지를 판단한다. 
		 */
		$.prototype.canSeek = function(time)
		{
			// FireFox에서 seekable 속성을 지원하지 않으므로 buffered 속성을 이용한다.
			var seekable = this.player[0].seekable ? this.player[0].seekable : this.player[0].buffered;
			
			for (var index = 0; index < seekable.length; index++)
			{
				var start = seekable.start(index);
				var end = seekable.end(index);
				
				if (time >= start && time <= end)
				{
					return true;
				}
			}
			
			return false;
		};
		
		/**
		 * 컨트롤을 렌더링한다.
		 */
		$.prototype.renderControls = function()
		{
			$.prototype.controls = $('<div class="vp-controls"></div>').appendTo($(this));
			
			this.controls.css('left', parseInt(this.css('width')) / 2 - parseInt(this.controls.css('width')) / 2);
			
			this.controls.hide();
			
			this.controls.playbutton = $('<div class="vp-controls-playbutton"></div>').appendTo(this.controls);
			
			this.controls.timelabel = $('<div class="vp-controls-timelabel"></div>').appendTo(this.controls);
			
			this.controls.timelabel.html('00:00:00');
			
			this.controls.progressoutline = $('<div class="vp-controls-progressoutline"></div>').appendTo(this.controls);
			
			this.controls.progressbar = $('<div class="vp-controls-progressbar"></div>').appendTo(this.controls.progressoutline);
			this.controls.bufferingbar = $('<div class="vp-controls-bufferingbar"></div>').appendTo(this.controls.progressoutline);
			
			this.controls.durationlabel = $('<div class="vp-controls-durationlabel"></div>').appendTo(this.controls);
			
			this.controls.durationlabel.html('00:00:00');
			
			this.controls.volumeoutline = $('<div class="vp-controls-volumeoutline"></div>').appendTo(this.controls);
			
			this.controls.volumebar = $('<div class="vp-controls-volumebar"></div>').appendTo(this.controls.volumeoutline);
		};
		
		/**
		 * 컨트롤의 이벤트를 연결한다.
		 */
		$.prototype.attachControlsEvent = function()
		{			
			this.controls.playbutton.click({container: this}, function(e)
			{
				var container = e.data.container;
				
				switch (container.isPlaying())
				{
					case true:
						
						container.pause();
						
						break;
						
					case false:
						
						container.play();
						
						break;
				}
			});
			
			this.controls.progressoutline.click({container: this}, function(e)
			{
				var container = e.data.container;
				var player = container.player;
				var controls = player.controls;
				
				var rate = parseInt((e.clientX - controls.progressoutline.offset().left) * 100 / (parseInt(controls.progressoutline.css('width')))) / 100;
				
				rate = rate > 1.0 ? 1.0 : rate < 0.0 ? 0.0 : rate;
				
				controls.progressbar.css('width', (parseInt(controls.progressoutline.css('width'))) * rate);
				
				var seekTime = player[0].duration * rate;
				
				if (container.canSeek(seekTime))
				{
					container.currentTime(seekTime);
				}
			});
			
			this.controls.volumeoutline.click({container: this}, function(e)
			{
				var container = e.data.container;
				var controls = container.player.controls;
				
				var rate = parseInt(e.layerX * 100 / (parseInt(controls.volumeoutline.css('width')))) / 100;
				
				rate = rate > 1.0 ? 1.0 : rate < 0.0 ? 0.0 : rate;
				
				controls.volumebar.css('width', (parseInt(controls.volumeoutline.css('width'))) * rate);
				
				container.volume(rate);
			});
		};
		
		/**
		 * 컨트롤을 표시한다.
		 */
		$.prototype.showControls = function()
		{
			this.controls.show(300, 'swing');
		};
		
		/**
		 * 컨트롤을 숨긴다.
		 */
		$.prototype.hideControls = function()
		{
			if (this.controls.css('display') == 'none') return;
			
			this.controls.hide(300, 'swing');
		};
		
		/**
		 * 컨트롤을 추가한다.
		 */
		$.prototype.addControl = function(css, clickParam, click)
		{
			var control = $('<div></div>').appendTo(this.controls);
			
			control.attr('class', css);
			
			control.click(clickParam, click);
			
			return control;
		};
		
		/**
		 * 재생율 바를 렌더링한다.
		 */
		$.prototype.renderProgress = function(rate, time)
		{
			var width = parseInt(this.player.controls.progressoutline.css('width')) * rate / 100;
			
			this.player.controls.progressbar.css('width', width);
			
			var hour = parseInt(time / 3600);
			hour = hour > 9 ? hour : '0' + hour;
			var minute = parseInt(time % 3600 / 60);
			minute = minute > 9 ? minute : '0' + minute;
			var second = parseInt(time % 3600 % 60);
			second = second > 9 ? second : '0' + second;
			
			this.player.controls.timelabel.html(hour + ':' + minute + ':' + second);
		};
		
		/**
		 * 버퍼링 바를 렌더링한다.
		 */
		$.prototype.renderBuffer = function(rate)
		{
			var width = parseInt(this.player.controls.progressoutline.css('width')) * rate / 100;
			
			this.player.controls.bufferingbar.css('width', width);
		};
		
		/**
		 * 미디어 재생 시간을 렌더링한다.
		 */
		$.prototype.renderDuration = function()
		{
			var duration = this.duration();
			
			var hour = parseInt(duration / 3600);
			hour = hour > 9 ? hour : '0' + hour;
			var minute = parseInt(duration % 3600 / 60);
			minute = minute > 9 ? minute : '0' + minute;
			var second = parseInt(duration % 3600 % 60);
			second = second > 9 ? second : '0' + second;
			
			this.player.controls.durationlabel.html(hour + ':' + minute + ':' + second);
		};

		/**
		 * 불륨 바를 렌더링한다.
		 */
		$.prototype.renderVolume = function(rate)
		{
			this.player.controls.volumebar.css('width', rate);
		};
		
		this.initialize(controls);
	};
})(jQuery);